package controllers;

import java.util.ArrayList;
import java.util.Arrays;

import play.cache.Cache;
import java.util.List;

import models.Attribute;
import models.FindExact;
import models.Location;
import models.FindSimilar;
import models.Global;
import models.Item;

import play.mvc.*;

/**
 * This Controller is the application's main controller, and handles are request.
 * @author Yaser
 */
public class Views extends Controller {

	/**
	 * This methods simply renders search.html, the index search page
	 * @author Yaser
	 */
    public static void search() {
        render();
    }
    
    /**
   	 * This method cathces all cals to searchResults where no parameters (keywords, longitude, latitude, nearby choice) are given.
   	 * It forwards to call to the proper searchResults method with all parameters as null.
   	 * @author Yaser
   	 */
       public static void searchResults() {
       	searchResults(null, null, null, null);
       }
    
    /**
   	 * This is the method that displays the results page for the keywords search. It renders searchResults.html
   	 * It does not load the actual results, as those are loaded dynamically by JavaScript using the searchResultsJS method.
   	 * If the parameters are null, they are loaded from the session cache
   	 * @param keywords Keywords for which to perform the search
   	 * @param latitude The user's latitude
   	 * @param longitude The user's longitude
   	 * @param nearby Whether the user wants to limit the search to cathedrals nearby
   	 * @author Yaser
   	 */
    public static void searchResults(String keywords, Double latitude, Double longitude, Boolean nearby) {
//    	System.out.println(nearby);
    	if(keywords == null)
    		keywords = Cache.get( session.getId() + "keywords", String.class);
    	else
    		Cache.set( session.getId() + "keywords", keywords);
    	
//    	System.out.println("latitude:" + latitude);
    	if(latitude != null)
    		Cache.set( session.getId() + "latitude", latitude);
    	if(longitude != null)
    		Cache.set( session.getId() + "longitude", longitude);
    	
    	List<Long> interests = Cache.get( session.getId() + "objects", List.class);
    	
    	if(nearby == null) {
    		nearby = Cache.get(session.getId() + "nearby", Boolean.class);
    		if(nearby == null) {
    			nearby = false;
    			Cache.set(session.getId() + "nearby", nearby);
    		}
    	}
    	else {
    		Cache.set(session.getId() + "nearby", nearby);
    	}
    	
        render(keywords, interests, nearby);
    }
    
    /**
	 * This methods loads the search results for the simple keywords search. It uses the searchResultsJS.html.
	 * It is called by JavaScript only from the view searchResults.html
	 * @author Yaser
	 */
    public static void searchResultsJS() {
    	String keywords = Cache.get( session.getId() + "keywords", String.class);
//    	System.out.println(keywords);
    	
    	Double longitude = Cache.get( session.getId() + "longitude", Double.class);
    	Double latitude = Cache.get( session.getId() + "latitude", Double.class);
    	Boolean nearby = Cache.get(session.getId() + "nearby", Boolean.class);
    	List <Item> objects = null;
    	
    	if(nearby && longitude != null && latitude != null) {
//    		System.out.println(Location.getMinLat(latitude, longitude));
//    		System.out.println(Location.getMinLat(latitude, longitude));
    		objects = Global.searchItems(keywords, Location.getMinLat(latitude, longitude), Location.getMaxLat(latitude, longitude), Location.getMinLon(latitude, longitude), Location.getMaxLon(latitude, longitude));
    	}
    	else {
    		objects = Global.searchItems(keywords, null, null, null, null);
    	}
    	
    	List<Long> interests = Cache.get( session.getId() + "objects", List.class);
    	
   
    	
        render(objects, keywords, interests);
    }
    
    

    /**
   	 * This is the method that displays information about a certain cathedral, using viewObject.html.
   	 * The findResults parameter is used whether to forward to either findResults() or to searchResults() when the user leaves the viewObject page through "Add to object of Interests" or "Back to results"
   	 * @param id The cathedral the user wants to view
   	 * @param nearby Whether the user opened this page from findResults or not. 
   	 * @author Yaser
   	 */
    public static void viewObject(Long id, boolean findResults) {
    	String keywords = Cache.get( session.getId() + "keywords", String.class);
    	Item object = Item.findById(id);
    	if(object == null)
    		searchResults();
    	else {
    		Double longitude = Cache.get( session.getId() + "longitude", Double.class);
        	Double latitude = Cache.get( session.getId() + "latitude", Double.class);
        	
        	String myLocation = "My+Location";
        	if(longitude != null && latitude != null) {
        		myLocation = "" + latitude + "," + longitude;
        	}
        	
    		render(keywords, object, findResults, myLocation);
    	}
    }
    
    /**
   	 * This is the method that displays the results for the similarity and exact search.
   	 * This method has not parameters as all data is loaded in the cache.
   	 * The method that store data in cache are addObject(), removeObject(), setAttributes() and setSearch()
   	 * @author Yaser
   	 */
    public static void findResults() {
    	String keywords = Cache.get( session.getId() + "keywords", String.class);
    	List<Long> objects = Cache.get( session.getId() + "objects", List.class);
    	List<Item> interests = new ArrayList<Item>();
    	
    	if(objects != null) {
    		for(Long o: objects) {
    			Item i = Item.findById(o);
    			if(i != null) {
    				interests.add(i);
    			}
    		}
    	}
    	
    	Long[] storedAttributesArray = Cache.get(session.getId() + "selectedAttributes", Long[].class);
    	List<Long> storedAttributes = new ArrayList<Long>();
    	if(storedAttributesArray != null) {
    		for(int i = 0; i < storedAttributesArray.length; i++) {
    			storedAttributes.add(storedAttributesArray[i]);
    		}
    		
    	}
    	
    	for(Long a: storedAttributes) {
    		Attribute att = Attribute.findById(a);
//    		System.out.println(att.item.getAttribute("label"));
    	}
    	
    	Boolean nearby = Cache.get(session.getId() + "nearby", Boolean.class);
    	if(nearby == null) {
    		nearby = false;
    	}

    	
    	Integer searchType = Cache.get(session.getId() + "searchType", Integer.class);
    	if(searchType == null || (searchType != null && (searchType != 1 && searchType != 2))) {
    		searchType = 1;
    		Cache.set(session.getId() + "searchType", searchType);
    	}
    	
//    	System.out.println(searchType);
    	render(interests, keywords, storedAttributes, nearby, searchType);
    }
    
    
    /**
   	 * This methods loads the search results for the similarity (exact) search. It uses the findResultsJS.html.
   	 * It is called by JavaScript only from the view findResults.html
   	 * @author Yaser
   	 */
    public static void findResultsJS() {
    	Long[] storedAttributes = Cache.get(session.getId() + "selectedAttributes", Long[].class);
    	
    	List<Item> objects = null;
    	if(storedAttributes != null) {
//    		System.out.println(storedAttributes.length);
    		List<Attribute> attributes = new ArrayList<Attribute>();
        	
        	for(int i = 0; i < storedAttributes.length; i++) {
        		attributes.add( (Attribute) Attribute.findById(  storedAttributes[i]   ));
        	}
        	
        	if(attributes.size() != 0) {
        		Integer searchType = Cache.get(session.getId() + "searchType", Integer.class);
//        		System.out.println(searchType);
            	if(searchType == null || (searchType != null && (searchType != 1 && searchType != 2))) {
//            		System.out.println(searchType);
            		searchType = 1;
            		Cache.set(session.getId() + "searchType", searchType);
            	}
            	
            	if(searchType == 1) {
            		List<Long> interests = Cache.get( session.getId() + "objects", List.class);
                	int count = 0;
                	if(interests != null) {
                		for(Long o: interests) {
                			Item i = Item.findById(o);
                			if(i != null) {
                				count++;
                			}
                		}
                	}
            		
            		objects = FindSimilar.work(attributes, count);
            	} else if (searchType == 2) {
            		objects = FindExact.work(attributes);
            	}
        		
        	}
    	}
    	
    	
    	
    	render(objects);
    }
    
    /**
   	 * This methods allows the user to add a cathedral to the objects of interests.
   	 * It forwards to searchResults() or to findResults() depending on the findResults parameter.
   	 * @param id The cathedral the user wants to add to the objects of interests
   	 * @param nearby Whether the user opened this page from findResults or not. 
   	 * @author Yaser
   	 */
    public static void addObject(Long id, boolean findResults) {
    	List<Long> objects = Cache.get( session.getId() + "objects", List.class);
    	Item object = Item.findById(id);
    	if(object!=null) {
    	
    		if(objects == null)
        		objects = new ArrayList<Long>();
        	if(!objects.contains(id))
        		objects.add(id);
        	Cache.set( session.getId() + "objects", objects);
        	
        	Long[] storedAttributesArray = Cache.get(session.getId() + "selectedAttributes", Long[].class);
        	List<Long> storedAttributes = new ArrayList<Long>();
        	if(storedAttributesArray != null) {
        		for(int i = 0; i < storedAttributesArray.length; i++) {
        			storedAttributes.add(storedAttributesArray[i]);
        		}
        	}
        	
			for(Attribute a: object.getCategoryAttributes()) {
				if(!storedAttributes.contains(a.getId())) 
    				storedAttributes.add(a.getId());
        		
        	}
    		
    		Cache.set(session.getId() + "selectedAttributes", storedAttributes.toArray( new Long[]{} ));
    	}
    	
    	
    	if(findResults)
    		findResults();
    	else
    		searchResults();
    }
    
    /**
   	 * This methods allows the user to remove cathedral from the objects of interests.
   	 * It forwards to findResults().
   	 * @param id The cathedral the user wants to remove from the objects of interests
   	 * @author Yaser
   	 */
    public static void removeObject(Long id) {
    	Item object = Item.findById(id);
    	
    	Long[] storedAttributesArray = Cache.get(session.getId() + "selectedAttributes", Long[].class);
    	ArrayList<Long> storedAttributes = new ArrayList<Long>();
    	if(storedAttributesArray != null) {
    		for(int i = 0; i < storedAttributesArray.length; i++) {
    			storedAttributes.add(storedAttributesArray[i]);
    		}
    		
    		
    		for(Attribute a: object.getCategoryAttributes()) {
    			if(storedAttributes.contains(a.getId())) 
    				storedAttributes.remove(a.getId());
        		
        	}
    		
    	}
    	
    	
    	Cache.set(session.getId() + "selectedAttributes", storedAttributes.toArray( new Long[]{} ) );
    	
    	
    	List<Long> objects = Cache.get( session.getId() + "objects", List.class);
    	if(objects == null)
    		objects = new ArrayList<Long>();
    	if(objects.contains(id))
    		objects.remove(id);
    	Cache.set( session.getId() + "objects", objects);
    	
    	
    	
    	findResults();
    }

    /**
   	 * This method allows to set which attributes a user is interested.
   	 * This methods is called through JavaScript by findResults.html.
   	 * @param id The cathedral the user wants to change the attributes for
   	 * @param attributes List of attributes selected
   	 * @author Yaser
   	 */
    public static void setAttributes(Long id, Long[] attributes) {
//    	System.out.println("fsdfsdf");
    	Item object = Item.findById(id);
    	List<Long> selectedAttributes;
    	if(attributes != null)
    		selectedAttributes = Arrays.asList(attributes);
    	else 
    		selectedAttributes = new ArrayList<Long>();
    	
    	Long[] storedAttributesArray = Cache.get(session.getId() + "selectedAttributes", Long[].class);
    	ArrayList<Long> storedAttributes = new ArrayList<Long>();
    	if(storedAttributesArray != null) {
    		for(int i = 0; i < storedAttributesArray.length; i++) {
    			storedAttributes.add(storedAttributesArray[i]);
    		}
    	}

    	
    	
    	for(Attribute a: object.getCategoryAttributes()) {
    		if(selectedAttributes.contains(a.getId())) {
    			if(!storedAttributes.contains(a.getId())) 
    				storedAttributes.add(a.getId());
    		}
    		else {
    			if(storedAttributes.contains(a.getId())) 
    				storedAttributes.remove(a.getId());
    		}
    	}
    	
    	Cache.set(session.getId() + "selectedAttributes", storedAttributes.toArray( new Long[]{} ) );
    	
    }
    
    /**
   	 * This method allows to set which search algorithm the user wants to use
   	 * This methods is called through JavaScript by findResults.html.
   	 * @param search The search algorithm. 1 for similarity search, 2 for exact search.
   	 * @author Yaser
   	 */
    public static void setSearch(Integer search) {
    	
    	if(search == 1 || search == 2) {
    		Cache.set(session.getId() + "searchType", search);
    	}
    	else {
    		Integer def = 1;
    		Cache.set(session.getId() + "searchType", def);
    	}
		
    }
    
    /**
   	 * Redirects to a larger version of the humb of an object. Uses the Item.getPicture method.
   	 * @param id The id of the cathedral the user wants to have an image of
   	 * @author Yaser
   	 */
    public static void getPicture(Long id){
    	Item object = Item.findById(id);
    	if(object != null)
    		redirect(object.getPicture(400));
    	
    }
    
   
}
